#pragma once

#include "Socket.hpp"
#include <sys/epoll.h>

using namespace socket_ns;
using namespace log_ns;

class EpollServer
{
private:
    const static int size = 128;
    const static int num = 128;

public:
    EpollServer(in_port_t port)
        : _port(port), _listenSocket(std::make_unique<TcpSocket>())
    {
        _listenSocket->BuildListenSocket(_port);
        _epfd = epoll_create(size);
        if (_epfd < 0)
        {
            LOG(FATAL, "epoll_create error: %d -> %s\n", errno, strerror(errno));
            exit(1);
        }
        LOG(INFO, "epoll create success, epfd: %d\n", _epfd);
    }

    void InitServer()
    {
        // 要先把 listen sockfd 添加到 epoll 模型中
        struct epoll_event ev;
        // ev.events = EPOLLIN  // LT 模式 
        ev.events = EPOLLIN | EPOLLET;  // ET 模式 
        ev.data.fd = _listenSocket->GetSockfd(); // 为了在事件就绪时，得到具体是哪一个 fd 就绪
        int n = ::epoll_ctl(_epfd, EPOLL_CTL_ADD, _listenSocket->GetSockfd(), &ev);
        if (n < 0)
        {
            LOG(FATAL, "epoll_ctl error: %d -> %s\n", errno, strerror(errno));
            exit(2);
        }
        LOG(INFO, "Add new sockfd %d success(epoll_ctl)\n", _listenSocket->GetSockfd());
    }

    const std::string EventsToString(uint32_t events)
    {
        std::string eventstr;
        if (events & EPOLLIN)
            eventstr += "EPOLLIN ";
        if (events & EPOLLOUT)
            eventstr += "| EPOLLOUT ";
        return eventstr;
    }

    void Accepter()
    {
        // 表示有新连接到来
        InetAddr peer;
        int sockfd = _listenSocket->Accepter(&peer);
        if (sockfd < 0)
        {
            LOG(WARNING, "Fail to get new link!");
            return;
        }
        LOG(INFO, "Get a new link, sockfd: %d -> [%s]\n", sockfd, peer.GetAddrStr().c_str());
        // 添加进 epoll，等待该 fd 的 IO 数据就绪
        struct epoll_event ev;
        ev.data.fd = sockfd;
        ev.events = EPOLLIN;
        ::epoll_ctl(_epfd, EPOLL_CTL_ADD, sockfd, &ev);
        LOG(INFO, "Add new sockfd %d success(epoll_ctl)\n", sockfd);
    }

    void HandlerIO(int fd)
    {
        // ET 工作模式下，需要循环读取 
        // 但为了和别的 fd 区分开，每个 fd 都需要有自己的缓冲区，不互相影响 
        // 引入协议，对每个缓冲区进行处理才能有效解包，提取到一个完整的报文 
        char buffer[1024];
        int m = ::recv(fd, buffer, sizeof(buffer) - 1, 0);
        if (m < 0)
        {
            LOG(ERROR, "recv error: %d -> %s\n", errno, strerror(errno));
            ::epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr);
            ::close(fd);
        }
        else if (m == 0)
        {
            LOG(INFO, "Client quit, close fd: %d\n", fd);
            // 先从 epoll 中移除该 fd
            ::epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr);
            // 再关闭 fd
            ::close(fd);
        }
        else
        {
            buffer[m] = 0;
            std::cout << buffer << std::endl;
            std::string response;
            std::string content = "<html><body><h1>Hello World!</h1></body></html>";
            response += "HTTP/1.0 200 OK\r\n";
            response += "Content-Type: text/html\r\n";
            response += "Content-Length: " + std::to_string(content.size()) + "\r\n\r\n";
            response += content;
            ::send(fd, response.c_str(), response.size(), 0);
        }
    }

    void HandlerEvent(int n)
    {
        // 就绪了 n 个就处理这 n 个呗
        for (int i = 0; i < n; ++i)
        {
            int fd = revs[i].data.fd;
            uint32_t revents = revs[i].events;
            LOG(INFO, "%d is ready -> %s\n", fd, EventsToString(revents).c_str());
            // 事件派发
            if (revents & EPOLLIN)
            {
                // 读事件就绪
                if (fd == _listenSocket->GetSockfd())
                {
                    // 监听套接字读就绪
                    Accepter();
                }
                else
                {
                    // IO 套接字读就绪
                    HandlerIO(fd);
                }
            }
        }
    }

    void Loop()
    {
        int timeout = -1;
        while (true)
        {
            int n = ::epoll_wait(_epfd, revs, num, timeout);
            switch (n)
            {
            case 0:
                LOG(DEBUG, "Epoll Time Out!\n");
                break;
            case -1:
                LOG(ERROR, "Poll Error: %d -> %s\n", errno, strerror(errno));
                break;
            default:
                LOG(INFO, "Haved event ready, n: %d\n", n);
                // HandlerEvent(n);
                break;
            }
        }
    }

    ~EpollServer()
    {
        if (_epfd >= 0)
            ::close(_epfd);
        _listenSocket->CloseSockfd();
    }

private:
    int _epfd;
    in_port_t _port;
    struct epoll_event revs[num]; // 获取就绪 IO 的缓冲区
    std::unique_ptr<Socket> _listenSocket;
};